#ifndef LEXER_HPP
#define LEXER_HPP

#include <istream>
#include <stdio.h>
#include <string.h>
#include <string>

enum TokenType {
    T_INT,
    T_STRING,
    T_NAME,
    
    T_LAMBDA,
    T_LAM_DEF,
    T_ENDL,
    T_ASN,
    
    T_LEFT_PAR,
    T_RIGHT_PAR,
    T_LEFT_BRACE,
    T_RIGHT_BRACE,
    T_LEFT_BRACKET,
    T_RIGHT_BRACKET,
    T_COLON,
    T_COMMA,
    T_ARROW,
    
    T_PLUS,
    T_MINUS,
    T_DIV,
    T_MULT,

    T_ADD_ASN,
    T_SUB_ASN,
    T_DIV_ASN,
    T_MUL_ASN,

    T_KW_IF,
    T_KW_ELSE,
    T_KW_WHILE,
    T_KW_RETURN,
    T_KW_PRINT,
    T_KW_AND,
    T_KW_OR,
    T_KW_TYPE,
    T_KW_DATA,
    T_KW_NEW,

    T_EQU,
    T_LT,
    T_LE,
    T_GT,
    T_GE,

    LOGIC_OR,
    T_LOGIC_NOT,
    LOGIC_AND,
    BIT_OR,
    BIT_XOR,
    BIT_AND,
    LEFT_SHIFT,
    RIGHT_SHIFT,

    /* About Types. */
    T_KW_VAR,
    T_KW_VAL,
    T_KW_DEF,

    T_CONST_UNIT,
    T_CONST_TRUE,
    T_CONST_FALSE,
};

class Token {
public:
    TokenType   _tt;
    const char* _value;
    int         _length;
    
    Token(TokenType tt, const char* v, int length);
    bool equals(const char* s);
    
    void print();
};

enum State {
    INIT,
    INTEGER,
    STRING,
    NAME,
    OP_SLASH,
    
    OP_ADD,
    OP_SUB,
    OP_MUL,
    OP_DIV,

    OP_AND,
    OP_OR,
    OP_XOR,

    OP_EQ,
    OP_LT,
    OP_GT,
    
    OP_COLON
};

class Lexer {
private:
    static const int BUF_LEN = 1024;
    char*   _s;
    int     _index;
    State   _state;
    int     _length;
    std::istream* _input;

    explicit Lexer(std::istream& stream);
    bool read_to_buf();
public:
    static Lexer* from_file(const char* filename);
    static Lexer* from_string(const std::string& str);
    ~Lexer();
    
    Token* next();
    
    inline char   getchar();
    inline void   ungetc();
    Token* filter_keyword(Token* t);
};

#endif

